﻿using Microsoft.AspNetCore.Http;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace BDRUtils.Utility
{
    /// <summary>
    /// 断点下载
    /// </summary>
    public static class FileRangeDownload
    {
        /// <summary>
        /// 断点下载：
        /// 标准规范：HTTP_RANGE: bytes=0-801 //一般请求下载整个文件是bytes=0- 或不用这个头
        /// </summary>
        /// <param name="context">上下文</param>
        /// <param name="fullpath">文件完整路径</param>
        /// <returns></returns>
        public static async Task<long> RangeDownload(HttpContext context, string fullpath)
        {
            long size, start, end, length, fp = 0;
            using (StreamReader reader = new StreamReader(File.OpenRead(fullpath)))
            {

                size = reader.BaseStream.Length;
                start = 0;
                end = size - 1;
                length = size;
                context.Response.Headers.Add("Accept-Ranges", "0-" + size);

                if (!string.IsNullOrEmpty(context.Request.Headers["HTTP_RANGE"]))
                {
                    long anotherStart = start;
                    long anotherEnd = end;
                    string[] arr_split = context.Request.Headers["HTTP_RANGE"].FirstOrDefault().Split(new char[] { Convert.ToChar("=") });
                    string range = arr_split[1];

                    if (range.IndexOf(",") > -1)
                    {
                        context.Response.Headers.Add("Content-Range", "bytes " + start + "-" + end + "/" + size);
                        context.Response.StatusCode = 416;
                        context.Response.StatusCode = 416;
                        await context.Response.WriteAsync("Requested Range Not Satisfiable");
                    }

                    if (range.StartsWith("-"))
                    {
                        // The n-number of the last bytes is requested
                        anotherStart = size - Convert.ToInt64(range.Substring(1));
                    }
                    else
                    {
                        arr_split = range.Split(new char[] { Convert.ToChar("-") });
                        anotherStart = Convert.ToInt64(arr_split[0]);
                        long temp = 0;
                        anotherEnd = (arr_split.Length > 1 && Int64.TryParse(arr_split[1].ToString(), out temp)) ? Convert.ToInt64(arr_split[1]) : size;
                    }

                    anotherEnd = (anotherEnd > end) ? end : anotherEnd;

                    if (anotherStart > anotherEnd || anotherStart > size - 1 || anotherEnd >= size)
                    {

                        context.Response.Headers.Add("Content-Range", "bytes " + start + "-" + end + "/" + size);
                        context.Response.StatusCode = 416;
                        await context.Response.WriteAsync("Requested Range Not Satisfiable");
                    }
                    start = anotherStart;
                    end = anotherEnd;

                    length = end - start + 1; // Calculate new content length
                    fp = reader.BaseStream.Seek(start, SeekOrigin.Begin);
                    context.Response.StatusCode = 206;
                }
            }
            // Notify the client the byte range we'll be outputting
            context.Response.Headers.Add("Content-Range", "bytes " + start + "-" + end + "/" + size);
            context.Response.Headers.Add("Content-Length", length.ToString());
            // Start buffered download
            await context.Response.SendFileAsync(fullpath, fp, length);
            return fp;
        }
    }
}
